home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Development Platforms / Apple II / Essentials / rTutors / Detailed.Docs / Rez.102 < prev    next >
Encoding:
Text File  |  1990-06-28  |  24.7 KB  |  470 lines  |  [TEXT/pdos]

  1. Rez 102
  2. by: Tim Swihart
  3.  
  4.  
  5. This time around, we'll look at Part 2 of the resource tutorial, naming 
  6. conventions, and places to go for more detailed information.  This one
  7. should be fairly short since the differences between Part 1 and Part 2 of
  8. the tutorial are minor and we've already laid a pretty solid ground work
  9. for the basics of resources.
  10.  
  11. For those of you who are still wondering why the changes from one part of 
  12. the resource tutorial to the next seem to be pretty small, it's on purpose.  
  13. This allows you to see the minimum amount of code needed to move from 
  14. one phase to the next.  It allows you to figure out which parts of the
  15. code are needed to start the tools, which parts are needed to open a
  16. window, which parts are needed to insert a menu bar, etc.  By breaking the
  17. application into smaller pieces, it's a lot easier to digest.  Many of you
  18. may want to just grab the complete app and figure it out for yourself,
  19. others would prefer to do it in increments so that they don't waste time
  20. trying to decipher some one else's thought process.  :-) 
  21.  
  22. The rest of this file assumes that you've already read (and more or less 
  23. understood) "Rez 101" which I uploaded earlier.  If you're still lost and
  24. this file only makes it worse, then either send email or post a plea for
  25. help.  If I don't know you're lost, I can't help (and neither can any of
  26. the other developers/programmers/hackers/etc that can help).
  27.  
  28.  
  29. What's New in Part 2?
  30.  
  31. Part 1 showed you how to start up the tools using a single tool call 
  32. (StartUpTools) instead of a series of routines (like we used to have to
  33. do before System Disk 5.0 saved us from the drudgery).  In Part 1, the 
  34. application started the tools, beeped three times, and shutdown the tools, 
  35. returning you to wherever you launched the app from.  This time around, 
  36. we'll do all of that, but AFTER the tools are started and BEFORE the three 
  37. calls to SysBeep, we'll insert the application's menu bar.
  38.  
  39. We'll still shutdown afterwards, so you'll never get a chance to do
  40. anything else in this version of the application.  Part 2.5 adds an Event
  41. Loop to the application so that we can play with Desk Accessories, pick
  42. menu items, etc.
  43.  
  44.  
  45. What's on the Menu (Bar)?
  46.  
  47. Prior to System Disk 5.0, menus were inserted one at a time in a loop.  If 
  48. your menu bar had ten menus, you had to make the tool call "InsertMenu" 
  49. ten times.  Not all that hard, but it could be easier.  Menus also had to be 
  50. described in a weird text format (clearly documented in several outstanding 
  51. books on e subject such as the Addison-Wesley "Programmer's Introduction 
  52. to the Apple IIGS" and the Apple IIGS Toolbox Reference Volume 1).
  53.  
  54. Wouldn't it be nice if you could just type in your menus, using a tools that 
  55. made it look and feel like you were editing REAL menus, not just a bunch of 
  56. text laying around?  GeneSys (from Simple Software Systems International) 
  57. and several other tools (AppMaker/GS, DesignMaster, etc) allow you to 
  58. create your menus in a format that looks like you're really editing an actual 
  59. menu!  Sure does make it a lot easier to see what your menus will look like 
  60. (and to tweak them before they're really part of your application).
  61.  
  62. But what if you suddenly wanted to have your menus in French?  Or you 
  63. wanted the text to be in Pig Latin?  Using the "old way" of doing things, 
  64. you'd have to edit your application's source, recompile, re-link, and run
  65. your application to see the end result (hmmm, maybe they looked better in 
  66. English after all).  Using power tools like GeneSys, and taking full
  67. advantage of resources, your menus are NOT part of your application source
  68. code - they're resources (which are discrete units of data, right?
  69. [say, "right!"]).  That means you can simply edit the text in your menu
  70. resources and re-run your application!  No compiling, no linking, no
  71. waiting, and the editor looks like a menu editor should instead of just
  72. another text editor.
  73.  
  74.  
  75. Look at the Shiny New Line:
  76.  
  77. If you compare the last chunk of source code (the routine named "main) in 
  78. the rTutor.CC file from Part 2 to the same chunk from Part 2, you'll notice 
  79. that I added ONE line of code to it.  That line of code simply calls the 
  80. procedure that inserts the menu bar and all of its menus for us.  By "hiding" 
  81. the code to actually insert the menu bar in another area of the source code,
  82. I was able to make the "main" routine far more readable.  Look at the handful 
  83. of lines of source that are in Part 2 and you can immediately tell what's 
  84. going on.  Imagine if you expanded each routine right there instead of using 
  85. separate routines - it would be MUCH less obvious what was going on!
  86.  
  87. Readability is a BIG concern - especially when you're publishing your source 
  88. for the world to learn from.  If they can't read it, they can't learn very
  89. well.
  90.  
  91.  
  92. Naming Conventions are Your Friends!:
  93.  
  94. Despite what some folks say, it's important that you follow some sort of 
  95. naming conventions in your source code so that it's easier to read.  You may 
  96. know NOW what all of it does, but in a few weeks or months when you come 
  97. back to fix a bug, you'll have forgotten parts of it.  It's also very
  98. important to have easily readable code if it's going to be published (in an
  99. article for example), turned in for a grade (do you really want the
  100. instructor to have to guess what you're doing?), or used by others (who may
  101. have to fix your bugs).
  102.  
  103. In the source code I write, I follow several conventions to make things 
  104. easier.  The first one is to start all global variables with a lower case
  105. "g" (for "global").  This lets me quickly tell if I'm assigning an important
  106. value to a local variable (where it can quickly get lost) or to a global
  107. variable (where it will live forever).  I also capitalize the "words" within
  108. a global variables name (i.e.:  gToolListRef) - notice how the words are run
  109. together in my global variables?  Local variables have their "words"
  110. separated with an underscore (i.e.: my_mbar_hndl) and are all lower case.
  111. That gives me three different ways to tell at a glance whether a variable
  112. is global or local.  You may want to use only one or two ways instead of all
  113. three - do what ever works for you, just be consistent.
  114.  
  115. All constants within my source start with a lower case "k" (for "konstant").  
  116. why not a lower case "c"?  Because I picked this up from somebody else and 
  117. they used a "k" (not all conventions make a lot of sense, but they are 
  118. conventions...)  :-)   Constants, like globals variables, have their "words"
  119. run together with each "word" starting with an upper case letter (i.e:  
  120. "kShowClipItem").
  121.  
  122. Anywhere in my source that I'd normally imbed a hard-coded number (such 
  123. as passing a menu ID to FixAppleMenu), I use a constant.  All of my 
  124. constants are defined at the top of the file so that I can easily find them.  
  125. They all have a comment after them to job my memory about what they're 
  126. for.  I also tend to group them logically - all menu ID constants in one
  127. group, all menu items ID's for the first menu in the next group, all menus
  128. item ID's for the second menu in the next group, etc.  I also tend to
  129. separate major sections of constants from one another with a dividing
  130. line (in a comment).  This lets me group all of the smaller groups of
  131. menu-related constants into one area, window-related constants into
  132. another, etc.  All of these larger groups of constant come one behind
  133. the other in near the top of the file.
  134.  
  135. I generally add new constants to the end of the constants area so that I can 
  136. tell faster what's different from one version of a source file to the next.
  137. (the new stuff will be at the bottom of the constants section).
  138.  
  139. I generally start my procedures off with the characters "do_" - it makes the 
  140. source where I call those routines more readable (i.e.:  "do_make_menus" is 
  141. more readable than "menus" or even "make_menus").  Like local variables, I 
  142. name my procedures with all lower case letters and separate the "words" 
  143. within their names by underscores.
  144.  
  145. Each routine in the source file is separated from the preceding one by a 
  146. dividing line (in a comment) to make it more obvious where one routine 
  147. starts and another ends.
  148.  
  149. If you see a variable that STARTS with an underscore, then it's a "system" 
  150. variable - i.e.:  it's defined for us by the language author.  In the
  151. rTutor.cc file, you can find a couple such variables ("_ownerid" and
  152. "_toolerr").  Both of these are defined by Start.Root and are given values
  153. by either Start.Root (in the case of "_ownerID" which is simply the app's
  154. Memory Manager user ID) or the library (in the case of "_toolerr" which is
  155. used after all tool calls to save whatever error the call returned).
  156.  
  157. DO NOT NAME ANY OF YOUR OWN VARIABLES WITH A LEADING UNDERSCORE!!!
  158.  
  159. You'll confuse yourself into thinking they're "system" variables and you'll
  160. confuse others who have to read your source!  :-)
  161.  
  162. While we're on the subject, take full advantage of the pre-defined constants
  163. that are in the interface files.  Things like using "refIsResource" instead
  164. of "2" makes a LOT of difference when you're trying to see if the parameters
  165. to a call are right.  One is intuitive ("refIsResource") and one is asking
  166. for trouble (what's "2" for anyways?).  The Toolbox Reference Manuals have
  167. a summary of all constants related to a particular tool Manager at the end
  168. of each chapter.  Each of those constants SHOULD be in the interfaces (if
  169. they're not, report it to Apple via the interfaces bug report form in the
  170. Release Notes for "APW Tools & Interfaces").
  171.  
  172.  
  173. Now How Do We Do Menus?
  174.  
  175. Using System Disk 5.0 and up, we can insert an ENTIRE menu bar, with a 
  176. virtually unlimited number of menus (they really should fit on the screen, 
  177. but that's about the only limit) can be inserted with ONE tool call!  Since 
  178. we're only interested in using resources, I won't describe how this call
  179. could be used with hard-coded menu descriptions (after all, this is the
  180. 90's, the old way is passe').  :-)
  181.  
  182. Once we have our menu bar resource created (using a desktop tool like 
  183. GeneSys or the resource compiler [Rez] from APDA), we can insert it into the 
  184. running application by calling NewMenuBar2 (remember the "2" on the end, 
  185. leave it off and you're making a VERY different tool call!).  NewMenuBar2 
  186. requires three parameters as input and returns a handle to the newly 
  187. created menu bar.  The three parameters are:
  188.  
  189. 1) A reference descriptor (which I defined in Rez 101) to tell whether we're 
  190. using a resource, handle, or pointer to reference the menu bar.  Naturally, 
  191. we're using a resource, so we use the Apple-provided constant 
  192. "refIsResource" (this lets us not worry about what the actual value is that 
  193. goes here - it also makes the code a LOT more readable).  This parameter is
  194. a "word" in size (i.e.:  it takes up two bytes on the stack).
  195.  
  196. 2) The reference to the menu bar.  Parameter #1 described the reference 
  197. and Parameter #2 is the reference - be sure you understand the difference 
  198. (one of them describes something and the other IS something).  Since we're 
  199. using resources, this reference is simply the resource ID of the menu bar 
  200. resource.  There is a standard type defined for menu bar resources and you 
  201. HAVE to use it or this call won't be able to find your menu bar in the 
  202. resource fork.  Remember, ALL resources are identified by the combination 
  203. of BOTH their type and their ID.  This parameter is a "long" in size (i.e.:
  204. it takes up four bytes on the stack).  Pay attention here!  It's easy to 
  205. accidentally pass only a word (especially if you're using C [we are] and
  206. don't have prototyped interfaces [we don't because APW C doesn't support
  207. them]).  If you only pass a word for this parameter, the tool call will
  208. still pull a LONG off the stack and all sorts of weird things can start
  209. happening.
  210.  
  211. 3) A pointer to the PORT of the window that you want the menu bar inserted 
  212. into. Huh?  We don't want our main menu in a window!  We want it at the 
  213. top of the screen!!!!  Relax, if you pass a NIL (four bytes that are zero),
  214. then NewMenuBar2 will insert the new menu bar in the "system menu bar" 
  215. (better known as the top of the screen)  <grin>.  Since you pass four bytes
  216. of zeros for a NIL, this parameter must be a "long" in size (it is).  If you
  217. do try to put a menu bar in a window, be sure you pass a pointer to the
  218. window's PORT - if you don't know what a graf port pointer is, then look it
  219. up in Toolbox Reference Volume 2 (and don't try to stick a menu bar in a
  220. window if you don't understand ports).   :-)
  221.  
  222.  
  223. What Else?
  224.  
  225. Actually, NewMenuBar2 will only read in your menu bar resource (and all 
  226. associated menus and menu items) and insert it.  You have to make a couple 
  227. of other calls to tell the Menu Manager that this newly inserted menu is the 
  228. "system" menu and to draw it on the screen.  (If you don't draw it, the user 
  229. can't see it).  :-)
  230.  
  231. First, we call "SetSysBar" and pass it the handle that we got back from 
  232. NewMenuBar2.  SetSysBar tells the Menu Manager that the menu handle you 
  233. just gave it is supposed to be treated as the system menu.  SetSysBar
  234. doesn't return anything.
  235.  
  236. Next, we call "SetMenuBar" and pass it a NIL.  This tells the Menu Manager 
  237. which menu bar is the "current" one.  Confused about system menus and 
  238. current menus?  Don't be - just think of what it takes for the Menu Manager 
  239. to keep track of the situation where you have a menu bar at the top of the 
  240. screen (the "system" menu bar) and a desk accessory (such as C. K. Haun's 
  241. "WriteIt!" NDA) that also have a menu bar in their window.  How does the 
  242. Menu Manager figure out whether the application or the desk accessory 
  243. should be told that a menu item was picked?  That's where "current" and 
  244. "system" menu bar meanings come into play.  If you're still confused, don't 
  245. worry about it.  Just make both of these calls and don't stick any menu bars 
  246. in windows, then you won't have to hassle with it.  :-)
  247.  
  248. So, we've inserted the menu bar, told the Menu Manager to make it the 
  249. "system" menu bar, and told the Menu Manager to also make it the "current" 
  250. menu bar.  Now do we draw it so the user can see it?
  251.  
  252. NO!  What kind of application would you have if it didn't support desk 
  253. accessories???!!!  Oh yeah, NDA's...  Forgot about them, didn't you
  254. (and only two paragraphs after I mentioned an NDA by name).  There are a few 
  255. applications out there that don't bother to support NDA's and that's an 
  256. incredible shame because it only takes ONE tool call to insert NDA's into
  257. your Apple menu!  Sure, you have to have certain tool sets started up first,
  258. but so what!  Odds are you'd have those tool sets started anyways, so all
  259. we're REALLY talking about IS just ONE tool call!
  260.  
  261. That "magic" tool call is "FixAppleMenu" and you pass it the menu ID of your 
  262. Apple menu.  Actually, you can insert NDA's on ANY menu (as my Two 
  263. Apples NDA showed years ago), but tradition (and the STRONG desire to have 
  264. an intuitive user interface) dictates that we only insert NDA's into the 
  265. "Apple" menu.
  266.  
  267. Does that mean we should just pass a "1" for the menu ID used in this call?  
  268. NO!!!!  There is NOTHING requiring the Apple menu to have an ID of 1!  In 
  269. fact, many very well known applications do NOT use "1" for the ID of the 
  270. Apple menu (I know, I made that rather bogus assumption in an early 
  271. version of Two Apples NDA <blush> and had to fix it later).  When you 
  272. created your menu bar, you had to give each menu in it a unique ID.  That's 
  273. the ID you pass to FixAppleMenu.
  274.  
  275. One more thing...  Did you ever notice that menus are all different widths 
  276. and lengths?  That they always seem to just "exactly" fit around the text of 
  277. the items within them?  Notice that when you created your menus, you 
  278. didn't do ANYTHING to indicate how wide or how long the menus should be?  
  279. Then how come the menus are all the right size?
  280.  
  281. Simple, you make a tool call and that tool measures all the text in each menu 
  282. and makes the menus long enough and wide enough to hold the contents of 
  283. each menu.  The tool call I'm referring to is "FixMenuBar" and it requires no 
  284. input parameters.  It does however return a "word" that tells you how tall 
  285. the menu is.  The Rez tutorial application puts the returned menu height in a 
  286. local variable (so it's lost as soon as the "do_make_menus" routine is 
  287. finished).  We never really use this number in the things we're doing, so 
  288. that's why I didn't bother to keep it around.
  289.  
  290.  
  291. Now Can I Draw the Menu Bar?
  292.  
  293. Yes, now that we've inserted the menu bar, told the Menu Manager that it 
  294. was both the "system" and the "current" menu bar, added NDA's to our Apple 
  295. menu, and made sure the heights and widths of the menus were all correct, 
  296. we can finally draw the menu bar!  You do this with....  (you guessed it)  
  297. "DrawMenuBar".  This call takes no parameters as input and returns nothing 
  298. (kind of like SysBeep, only more special purpose in nature).
  299.  
  300.  
  301. Menu Bars and Rez:
  302.  
  303. Notice I mentioned using a desktop tool like GeneSys to lay out your menus?  
  304. Notice that the tutorial source uses Rez?  Confused over which tool to use?  
  305. Don't be!!!  GeneSys is more intuitive than Rez for laying out menus if you 
  306. don't really understand what you're doing.  But, since I uploaded Rez source 
  307. for the menu bars, I'd better explain it here.
  308.  
  309. Before we jump into all the various types of resource needed to make a 
  310. menu bar work, let's first look at what's in a menu bar from the 
  311. application/user perspective.  The menu bar contains several menus (i.e.:
  312. it's a list of menus).  The menus each contain several menu items (i.e.:
  313. they're lists of items).  Each menu has a name and each item has a name.
  314. Menus and menu items also have some special attributes (whether or not
  315. they're disabled, whether or not the items have a command key equivalent,
  316. etc).
  317.  
  318. In English, this means we need to have two types of lists (one type is a
  319. list of menus and the other is a list of menu items), text strings for
  320. each menu and each item, and attributes for menus and items.  Remembering
  321. from Rez 101 that each type of resource is different and that Apple defined
  322. a bunch of "standard" resource types already, we can guess that we should
  323. have the following types of resources:
  324.  
  325. - rMenuBar  (where we list the menus in the menu bar)
  326. - rMenu        (where we list the menu items and attributes for each menu)
  327. - rMenuItem (where we describe each menu item)
  328. - rPString      (where we list the "text" for each menu or item)
  329.  
  330. (rPString means that we're using a resource [that's what the "r" is for]
  331. and it's a Pascal-style string [that's what the "P" is for] - because
  332. that's what the Menu Manager wants).
  333.  
  334. There are a couple of ways to lay out the Rez source for a complete set of 
  335. menu bar/menus/menu items.  My preference is to lay out the menu bar 
  336. itself (which is a simple list of the resource ID's of the menus in the menu 
  337. bar), then the first menu, then each item in that menu.  After the menu, but 
  338. BEFORE the first item is described, I list the rPString for the name of that 
  339. menu (just to keep it close to the menu definition).  After each item, but 
  340. BEFORE the next item, I list the "rPString" for that item (again, just to
  341. keep it close to the menu item's definition).
  342.  
  343. The alternate way is to list the menu bar, then the menus, then the menu 
  344. items, then all of the rPStrings.  I don't like this one because it pushes
  345. the rPStrings too far away from the menus they're with and I like keeping 
  346. things close together (to avoid unnecessary scrolling).  Which method you 
  347. use is up to you (you could even come up with your own).
  348.  
  349. I also define constants for ALL resource ID's used by the menu bar, menus, 
  350. menu items, and rPStrings!  Why?  Because it's a LOT easier to figure out 
  351. that "resource rMenu (kAppleMID)" and "resource rPString (kAppleMID)" go 
  352. together than it would be if "kAppleMID" were just a number.  It also helps 
  353. you figure out later which item is which number (gee, I want to insert an 
  354. item in the File menu right after "Save", but I'm not sure which one of
  355. these menus is the File menu and which menu item is "Save" and....).
  356. Remember, naming conventions are your friends!
  357.  
  358. Inside the Rez source file (rTutor.rez), near the beginning of the menu
  359. layout section, are several lines of comments that spell out the strategy
  360. for laying out menu bars, menus, and menu items in Rez using the strategy
  361. I prefer.  Read those six steps if you're still a little fuzzy about how to
  362. do all of this.  It looks like a lot of typing, but most of us just use
  363. cut and paste instead of typing new menus, menu items, etc.  Just be
  364. careful to edit ALL of the fields of the pasted menu item or you'll get
  365. some bizarre results...  :-)
  366.  
  367. In your C (or Asm or Pascal) source file, the constants for menu bars
  368. should be long's (i.e.:  four bytes) - that's why they have an "L" after
  369. them.  You'll be making toolbox calls using the resource ID's as references
  370. to your menu bar (such as NewMenuBar2).  The constants for menus and menu
  371. items should be WORDS (not longs, despite the fact that I accidently left
  372. "L" on the end of each of them in Part 2's C source <blush>).  Why?
  373. Because you'll be passing these numbers to tool calls such as FixAppleMenu
  374. that expect a word.  We'll cover this in more detail in Part 2.5 once we've
  375. put in the event loop.  If you're in doubt about whether to use a word or
  376. a long, look at the toolbox reference manual's description of the call
  377. you're going to make and pass what it expects.  The only languages that
  378. will check the sizes for you are Pascal and an ANSI C (but only if the
  379. interfaces that you use are "prototyped").  If you use smart enough
  380. assembler macros, it should be able to check for you also.
  381.  
  382.  
  383. Review Time:
  384.  
  385. OK, what did it take to go from Part 1 (start the tools, beep three times,
  386. shut down) to Part 2 (start the tools, insert a menu bar, beep three times,
  387. shut down)?  We had to add one line of source to "main" (right before the
  388. beeps) and we had to write the routine that we wanted to call from "main"
  389. (I'm referring of course to the routine named "do_make_menus").  We could
  390. have slipped the call to "do_make_menus" into the middle of the SysBeeps
  391. or at the end.  I didn't just put them at the beginning for the heck of it.
  392. Later, we'll replace the SysBeeps with real stuff that HAS to come AFTER
  393. the menu bar is inserted, so the SysBeeps sort of mark our place for us.
  394.  
  395.  
  396. Need More Info?
  397.  
  398. You can get a different explanation of how Part 2 works by reading the 
  399. comments in the rTutor.cc source code.  Be sure to read them in the order 
  400. the program runs AFTER it's compiled - i.e.:  read the comments in "main", 
  401. jump to the comments in "do_init_rom" when you hit that line, come back to 
  402. "main" after "do_init_rom" is done, go to the comments in "do_make_menus", 
  403. etc.  That way, you'll read the comments in the same order the program is 
  404. actually running - it should help...
  405.  
  406. You can also consult the following references for more information:
  407.  
  408. - Apple IIGS Toolbox Reference, Volume 1 (for details on SetSysBar, 
  409. SetMenuBar, FixAppleMenu, FixMenuBar, and DrawMenuBar).
  410.  
  411. - Apple IIGS Toolbox Reference, Volume 3 (for details on NewMenuBar2 and 
  412. menu bar resources).
  413.  
  414.  
  415. OK, So I Lied:
  416.  
  417. I told you at the beginning that this would be a short file compared to Rez 
  418. 101.  I just counted the number of words in this one and in Rez 101 
  419. (actually, my word processor counted the words, I'm lazy <grin>) and here's 
  420. how Rez 102 stacks up against Rez 101 size-wise:
  421.  
  422. Version        Num. Words       Num. Characters
  423. -------------------------------------------------
  424. Rez 101               4672                     26698
  425. Rez 102               4483                     24951
  426.  
  427.  
  428. Enjoy,
  429. Tim S.
  430.  
  431. P.S.:  Get Rez from APDA or Developer Tools Express if you don't have it yet 
  432. or if you have the old version that was in "Programming Tools & Interfaces 
  433. for APW v.1.1".  Rez is part of the new "APW Tools & Interfaces v.1.1" 
  434. package.  New customers should order part number "A0240LL/A" (price $50, 
  435. includes manual) and those of you who bought "Programming Tools & 
  436. Interfaces for APW" should order the "update" version by requesting part 
  437. number "A0241LL/A" (price $25, no new manual).  These packages include 
  438. three disks (one of which contains revised interfaces for APW/ORCA Asm 
  439. and APW C).  The other two disks contain:
  440.  
  441.   Rez (resource compiler)
  442.   DeRez (resource decompiler)
  443.  
  444.   LinkIIGS (scriptable linker, creates OMF 2.0/2.1, auto-express's)
  445.   Compact (converts OMF 1.0 to OMF 2.0 so files are smaller and faster)
  446.  
  447.   CrunchIIGS (converts ORCA/Pascal and ORCA/C compiled files for LinkIIGS)
  448.   DiskCheck (checks for block conflicts, bad bitmaps, etc)
  449.  
  450.   DumpObj (lets you inspect/disassemble OMF files)
  451.   Duplicate (copies files without losing resource forks)
  452.  
  453.   Equal (compares files byte by byte- can compare resource forks too)
  454.   Express (convert older apps to take advantage of ExpressLoad in System 5.x)
  455.  
  456.   Files (super fancy catalog utility)
  457.   Join (merge two files together - used by CrunchIIGS)
  458.  
  459.   MakeBin (convert linked apps to binary image for 8-bit stuff, etc)
  460.   MakeDirect (create custom sized stack segments for your apps)
  461.  
  462.   MakeLib (library management tool)
  463.   ResEqual (compares two resource forks, resource by resource)
  464.  
  465.   Search (finds text in files)
  466.   Canon (makes sure you use proper casing for tool calls)
  467.   Canon.dict (dictionary for Canon - supports 5.0.2 tool calls)
  468.  
  469.   (nice list, eh?)  GET 'EM!   ;-)
  470.